/**
 * 文件名称: socket.c
 * 摘    要: linux网络编程示例
 * 来    源: 自我总结
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019-07-07
 **/

#include "socket.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <errno.h>
#include <pthread.h>


// 服务器线程传参结构体
struct server_thread_parameter
{
    int (* server_function)(const int *);       // 服务器功能函数
    int server_port;        // 服务器监听端口号
};

// 客户端线程传参结构体
struct client_thread_parameter
{
    int (* client_function)(const int);       // 客户端功能函数
    char server_ip[INET_ADDRSTRLEN];            // 服务器ip
    int server_port;        // 服务器端口号
};

/************************************************************************
函数名称: tcp_server_thread
函数功能: 创建tcp server套接字并创建tcp server功能函数线程
函数参数: tcp_server_thread_parameter: tcp server 监听串口号
函数返回: 无
************************************************************************/
static int tcp_server_thread(const struct server_thread_parameter *tcp_server_thread_parameter)
{
    int ret = -1;
    int server_fd = -1;     // 服务器socket套接字
    struct sockaddr_in server_addr;     // 服务器socket地址
    int server_port = 0;        // 服务器监听port

    // 客户端套接字
    int client_fd = -1;     // 客户端套接字
    char client_ip[INET_ADDRSTRLEN] = {0};   // 用于保存客户端IP地址
    struct sockaddr_in client_addr;         // 用于保存客户端地址
    socklen_t client_addr_len = sizeof(client_addr);        // 必须初始化!!!

    pthread_t tcp_server_function_thread_id = -1;        // cp server功能函数线程ID
    unsigned char fail_num = 0;     // 创建线程失败次数

    server_port = tcp_server_thread_parameter->server_port;

    // 创建TCP套接字
    // SOCK_STREAM表示使用面向连接的socket，即使用TCP连接
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0)
	{
		perror("tcp_server_thread socket error");
		exit(-1);
	}

    // 初始化服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;       // 协议族
    server_addr.sin_port = htons(server_port);      // 服务器监听port
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // INADDR_ANY表示任何IP地址, 也可指定本机地址

    printf("tcp server listening port: [%d]...\n", server_port);

    // 绑定
    ret = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (0 != ret)
    {
        perror("tcp_server_thread bind error");
		close(server_fd);
        exit(-1);
    }

    // 监听
    // 10 表示：等待连接队列的最大长度
    ret = listen(server_fd, 10);
    if (0 != ret)
    {
        perror("tcp_server_thread listen error");
		close(server_fd);
        exit(-1);
    }

    printf("tcp server start, waiting for connection...\r\n");

    while (1)
    {
        // 获得一个已经建立的连接, 会阻塞进程，直到有客户端连接上来为止
        client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_fd < 0)
        {
            perror("tcp_server_thread accept error");

            continue;       // 获取连接失败，则重新获取
        }

        // 打印连接到服务器的客户端IP和port
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
        printf("-------------------------------------------------------\n");
        printf("tcp server accept new connection from [%s:%d]\n", client_ip, ntohs(client_addr.sin_port));
        printf("-------------------------------------------------------\n");

        // 连接上一个客户端就创建一个线程处理函数
        ret = pthread_create(&tcp_server_function_thread_id, NULL, \
                             (void *)tcp_server_thread_parameter->server_function, &client_fd);
        if (0 != ret)
        {
            perror("tcp_server_thread creat tcp_server_thread_parameter->server_function fail!\n");
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(tcp_server_function_thread_id);      // 线程分离，结束时自动回收资源

            //return 0;     // 这里不返回，否则不能接收多个客户端
        }
    }
}

/************************************************************************
函数名称: tcp_client_thread
函数功能: 创建tcp client套接字并创建tcp client功能函数线程
函数参数: tcp_client_thread_parameter: tcp client 参数结构体
函数返回: 无
************************************************************************/
static int tcp_client_thread(const struct client_thread_parameter *tcp_client_thread_parameter)
{
    int ret = -1;
    int client_fd = -1;     // 客户端socket套接字
    struct sockaddr_in server_addr;     // 服务器socket地址
    char server_ip[INET_ADDRSTRLEN] = {0};  // 服务器ip
    int server_port = 0;        // 服务器监听port

    memcpy(server_ip, tcp_client_thread_parameter->server_ip, 
            strlen(tcp_client_thread_parameter->server_ip));
    server_port = tcp_client_thread_parameter->server_port;

    while (1)
    {
        // 创建TCP套接字
        // SOCK_STREAM表示使用面向连接的socket，即使用TCP连接
        client_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (client_fd < 0)
        {
            perror("tcp_client_thread socket error");

            exit(-1);
        }

        // 初始化需要连接的服务器地址
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(server_port);      // 服务器port
        server_addr.sin_addr.s_addr = inet_addr(server_ip);     // 服务器ip
        
        // 打印连接信息
        printf("connecting to %s:%d ...\n", server_ip, server_port);

        // 连接指定的服务器
        ret = connect(client_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
        if (ret < 0)
        {
            perror("connect error");

            sleep(1);

            continue;
        }

        // 打印连接成功提示信息
        printf("connect to server[%s:%d] ok\n", server_ip, server_port);

        // 设置socket为非阻塞模式
        // ret = fcntl(client_fd, F_GETFL, 0);
        // fcntl(client_fd, F_SETFL, (ret | O_NONBLOCK));

        while (1)
        {
            // 执行tcp client功能函数
            // 只有执行成功才退出，其余情况均重连
            ret = tcp_client_thread_parameter->client_function(client_fd);
            if (0 == ret)       // 成功
            {
                close(client_fd);        // 关闭套接字

                return 0;
            }
            else    // 其余情况重连服务器
            {
                close(client_fd);        // 重连之前, 关闭套接字
                client_fd = -1;

                sleep(10);      // 延时一段时间再重连, 等待服务器重新启动
                
                printf("reconnect server...\n");

                break;
            }
        }
    }
}

/************************************************************************
函数名称: creat_tcp_server
函数功能: 创建tcp server线程
函数参数: tcp_server_function: tcp server的功能函数
          tcp_server_port: tcp server 监听串口号
函数返回: 成功: 返回0
          失败: 返回-1
************************************************************************/
int creat_tcp_server(int (* tcp_server_function)(const int *), const int tcp_server_port)
{
    int ret = -1;
    unsigned char fail_num = 0;     // 创建线程失败次数
    pthread_t tcp_server_thread_id = -1;        // tcp server线程ID

    struct server_thread_parameter tcp_server_thread_parameter = {0};

    tcp_server_thread_parameter.server_function = tcp_server_function;
    tcp_server_thread_parameter.server_port = tcp_server_port;

    while (1)
    {
        // 创建tcp server线程
        ret = pthread_create(&tcp_server_thread_id, NULL, \
                             (void *)tcp_server_thread, (void *)&tcp_server_thread_parameter);
        if (0 != ret)
        {
            perror("creat tcp_server_thread fail!\n");
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(tcp_server_thread_id);       // 线程分离，结束时自动回收资源
            
            return 0;
        }
    }
}

/************************************************************************
函数名称: creat_tcp_client
函数功能: 创建tcp client线程
函数参数: tcp_client_function: tcp client的功能函数
          tcp_server_ip: 需要连接的tcp server ip地址
          tcp_server_port: 需要连接的tcp server port
函数返回: 成功: 返回0
          失败: 返回-1
************************************************************************/
int creat_tcp_client(int (* tcp_client_function)(const int), 
                    const char *tcp_server_ip,  const int tcp_server_port)
{
    int ret = -1;
    unsigned char fail_num = 0;     // 创建线程失败次数
    pthread_t tcp_client_thread_id = -1;        // tcp server线程ID

    struct client_thread_parameter tcp_client_thread_parameter = {0};

    tcp_client_thread_parameter.client_function = tcp_client_function;
    memcpy(tcp_client_thread_parameter.server_ip, tcp_server_ip, strlen(tcp_server_ip));
    tcp_client_thread_parameter.server_port = tcp_server_port;

    while (1)
    {
        // 创建tcp client线程
        ret = pthread_create(&tcp_client_thread_id, NULL, \
                             (void *)tcp_client_thread, (void *)&tcp_client_thread_parameter);
        if (0 != ret)
        {
            perror("creat tcp_client_thread fail!\n");
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(tcp_client_thread_id);       // 线程分离，结束时自动回收资源
            
            return 0;
        }
    }
}

// 以下是功能函数示例代码, 根据项目情况实际修改
/************************************************************************
函数名称: tcp_server_function
函数功能: tcp server功能函数(根据项目实际需求进行相应修改)
函数参数: fd: 客户端套接字
函数返回: 成功: 返回0
          失败: 返回-1
************************************************************************/
int tcp_server_function(const int *fd)
{
    int ret = -1;
    int client_fd = *fd;
    unsigned char recv_buf[100] = {0};
    unsigned char send_buf[100] = {0};

    while(1)
    {
        memset(recv_buf, 0, sizeof(recv_buf));
        ret = recv(client_fd, recv_buf, sizeof(recv_buf), 0);      // 接收客户端发送过来的数据
        if (ret > 0)        // 接收到数据
        {
            printf("tcp server recv data from client: [%s]\n", recv_buf);        // 打印接收到的数据

            memset(send_buf, 0, sizeof(send_buf));
            memcpy(send_buf, "hello! I'm tcp server!", strlen("hello! I'm tcp server!"));
            send(client_fd, send_buf, strlen((const char *)send_buf), 0);
            printf("tcp server send data to client: %s\n", send_buf);
        }
        else if (0 == ret)      // socket 正常关闭
        {
            printf("client socket closed!\n");

            break;      // 退出，关闭socket
        }
        else if (-1 == ret)     // 非阻塞模式下表示缓冲区空
        {
        }
        else
        {
            // 错误处理
            if (EAGAIN != errno)
            {
                printf("recv error ret = %d", ret);

                break;      // 退出，关闭socket
            }
        }
    }

    close(client_fd);        // 当前连接断开后，关闭套接字

    return 0;
}

/************************************************************************
函数名称: tcp_client_function
函数功能: tcp client功能函数
函数参数: client_fd: 客户端套接字
函数返回: 成功: 返回0
          失败: 返回-1/对应错误码
************************************************************************/
int tcp_client_function(const int client_fd)
{
    int ret = -1;
    unsigned char recv_buf[100] = {0};
    unsigned char recv_buf_len = 0;
    unsigned char send_buf[100] = {0};
    unsigned char send_buf_len = 0;

    while (1)
    {
        memset(send_buf, 0, sizeof(send_buf));
        send_buf_len = strlen("hello server");
        memcpy(send_buf, "hello server", send_buf_len);
        ret = send(client_fd, send_buf, send_buf_len, 0);        // 向服务器发送数据
        if (-1 == ret)
        {
            perror("send error");
        }

        recv_buf_len = 0;
        memset(recv_buf, 0, sizeof(recv_buf));
        recv_buf_len = recv(client_fd, recv_buf, sizeof(recv_buf), 0);        // 接受服务器发送过来的数据

        if (recv_buf_len > 0)        // 接收到数据
        {
            printf("tcp client recv data from server: [%s]\n", recv_buf);        // 打印接收到的数据

            // 解析接收数据
        }
        else if (0 == recv_buf_len)      // socket 正常关闭
        {
            printf("server socket closed!\n");

            return -1;      // 服务器关闭, 退出函数, 重连服务器
        }
        else if (-1 == recv_buf_len)     // 非阻塞模式下表示缓冲区空
        {
        }
        else
        {
            // 错误处理
            if (EAGAIN != errno)
            {
                printf("recv error ret = %d", ret);

                return -1;      // 异常情况, 退出函数, 重连服务器
            }
        }

        sleep(5);
    }

    return -1;
}